/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.rational.parser.operation;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.TreeSet;
import org.rational.parser.expression.Expression;
import org.rational.parser.operation.function.DefaultFunctions;
import org.rational.parser.type.DefaultTypes;
import org.rational.parser.type.Type;

/**
 *
 * @author Overmind
 */
public abstract class DefaultOperations
{
    private static TreeSet<Operation> operations = null;
    
    public static final BinaryOperation OR =
	    new BinaryOperation<Boolean, Boolean, Boolean>(1, "|",
	    DefaultTypes.LOGICAL, DefaultTypes.LOGICAL, DefaultTypes.LOGICAL)
    {
	@Override
	public Boolean calculate(Boolean first, Boolean second)
	{
	    return first | second;
	}
    };
    
    public static final BinaryOperation XOR =
	    new BinaryOperation<Boolean, Boolean, Boolean>(2, "^",
	    DefaultTypes.LOGICAL, DefaultTypes.LOGICAL, DefaultTypes.LOGICAL)
    {
	@Override
	public Boolean calculate(Boolean first, Boolean second)
	{
	    return first ^ second;
	}
    };
    
    public static final BinaryOperation AND =
	    new BinaryOperation<Boolean, Boolean, Boolean>(3, "&",
	    DefaultTypes.LOGICAL, DefaultTypes.LOGICAL, DefaultTypes.LOGICAL)
    {
	@Override
	public Boolean calculate(Boolean first, Boolean second)
	{
	    return first & second;
	}
    };
    
    public static final BinaryOperation EQUALS_TEXTUAL =
	    new BinaryOperation<Boolean, String, String>(4, "=",
	    DefaultTypes.LOGICAL, DefaultTypes.TEXTUAL, DefaultTypes.TEXTUAL)
    {
	@Override
	public Boolean calculate(String first, String second)
	{
	    return first.equals(second);
	}
    };
    
    public static final BinaryOperation EQUALS_NUMERIC = 
	    new BinaryOperation<Boolean, BigDecimal, BigDecimal>(4, "=",
	    DefaultTypes.LOGICAL, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public Boolean calculate(BigDecimal first, BigDecimal second)
	{
	    return first.equals(second);
	}
    };
    
    public static final BinaryOperation EQUALS_LOGICAL = 
	    new BinaryOperation<Boolean, Boolean, Boolean>(4, "=",
	    DefaultTypes.LOGICAL, DefaultTypes.LOGICAL, DefaultTypes.LOGICAL)
    {
	@Override
	public Boolean calculate(Boolean first, Boolean second)
	{
	    return first.equals(second);
	}
    };
    
    public static final BinaryOperation NOT_EQUALS_TEXTUAL =
	    new BinaryOperation<Boolean, String, String>(4, "!=",
	    DefaultTypes.LOGICAL, DefaultTypes.TEXTUAL, DefaultTypes.TEXTUAL)
    {
	@Override
	public Boolean calculate(String first, String second)
	{
	    return !first.equals(second);
	}
    };
    
    public static final BinaryOperation NOT_EQUALS_NUMERIC = 
	    new BinaryOperation<Boolean, BigDecimal, BigDecimal>(4, "!=",
	    DefaultTypes.LOGICAL, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public Boolean calculate(BigDecimal first, BigDecimal second)
	{
	    return !first.equals(second);
	}
    };
    
    public static final BinaryOperation NOT_EQUALS_LOGICAL = 
	    new BinaryOperation<Boolean, Boolean, Boolean>(4, "!=",
	    DefaultTypes.LOGICAL, DefaultTypes.LOGICAL, DefaultTypes.LOGICAL)
    {
	@Override
	public Boolean calculate(Boolean first, Boolean second)
	{
	    return !first.equals(second);
	}
    };
    
    public static final BinaryOperation LESS_EQUALS =
	    new BinaryOperation<Boolean, BigDecimal, BigDecimal>(5, "<=",
	    DefaultTypes.LOGICAL, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public Boolean calculate(BigDecimal first, BigDecimal second)
	{
	    return first.compareTo(second) <= 0;
	}
    };
    
    public static final BinaryOperation LARGE_EQUALS =
	    new BinaryOperation<Boolean, BigDecimal, BigDecimal>(5, ">=",
	    DefaultTypes.LOGICAL, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public Boolean calculate(BigDecimal first, BigDecimal second)
	{
	    return first.compareTo(second) >= 0;
	}
    };
    
    public static final BinaryOperation LESS =
	    new BinaryOperation<Boolean, BigDecimal, BigDecimal>(6, "<",
	    DefaultTypes.LOGICAL, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public Boolean calculate(BigDecimal first, BigDecimal second)
	{
	    return first.compareTo(second) < 0;
	}
    };
	
    public static final BinaryOperation LARGE =
	    new BinaryOperation<Boolean, BigDecimal, BigDecimal>(6, ">",
	    DefaultTypes.LOGICAL, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public Boolean calculate(BigDecimal first, BigDecimal second)
	{
	    return first.compareTo(second) > 0;
	}
    };
    
    public static final BinaryOperation PLUS =
	    new BinaryOperation<BigDecimal, BigDecimal, BigDecimal>(7, "+",
	    DefaultTypes.NUMERIC, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public BigDecimal calculate(BigDecimal first, BigDecimal second)
	{
	    return first.add(second);
	}
    };
    
    public static final BinaryOperation CONCAT =
	    new BinaryOperation<String, String, String>(7, "+",
	    DefaultTypes.TEXTUAL, DefaultTypes.TEXTUAL, DefaultTypes.TEXTUAL)
    {
	@Override
	public String calculate(String first, String second)
	{
	    return first + second;
	}
    };
    
    public static final BinaryOperation MINUS =
	    new BinaryOperation<BigDecimal, BigDecimal, BigDecimal>(7, "-",
	    DefaultTypes.NUMERIC, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public BigDecimal calculate(BigDecimal first, BigDecimal second)
	{
	    return first.add(second.negate());
	}
    };
    
    public static final BinaryOperation MULTIPLY =
	    new BinaryOperation<BigDecimal, BigDecimal, BigDecimal>(8, "*",
	    DefaultTypes.NUMERIC, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public BigDecimal calculate(BigDecimal first, BigDecimal second)
	{
	    return first.multiply(second);
	}
    };
    
    public static final BinaryOperation DIVIDE =
	    new BinaryOperation<BigDecimal, BigDecimal, BigDecimal>(8, "/",
	    DefaultTypes.NUMERIC, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public BigDecimal calculate(BigDecimal first, BigDecimal second)
	{
            return first.divide(second, MathContext.DECIMAL128);
        }      
    };
    
    public static final BinaryOperation REMAINDER =
	    new BinaryOperation<BigDecimal, BigDecimal, BigDecimal>(8, "%",
	    DefaultTypes.NUMERIC, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public BigDecimal calculate(BigDecimal first, BigDecimal second)
	{
	    return first.remainder(second);
	}
    };
    
    public static final UnaryPrefixOperation NOT =
	    new UnaryPrefixOperation<Boolean, Boolean>(9, "!",
	    DefaultTypes.LOGICAL, DefaultTypes.LOGICAL)
    {
	@Override
	public Boolean calculate(Boolean first)
	{
	    return !first;
	}
    };
    
    public static final UnaryPrefixOperation NEGATIVE =
	    new UnaryPrefixOperation<BigDecimal, BigDecimal>(9, "-",
	    DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
	@Override
	public BigDecimal calculate(BigDecimal first)
	{
	    return first.negate();
	}
    };
    
    public static final Operation SCOPE =
	    new Operation(11, new Expression("(?)"), DefaultTypes.CUSTOM, 
	    new Type[]{DefaultTypes.CUSTOM})
    {
	@Override
	protected Object calculateOfObjects(Object[] arguments)
	{
	    return arguments[0];
	}
    };
    
    private static void init()
    {
	operations = new TreeSet<>();
	
	operations.add(OR);   
	operations.add(XOR);   
	operations.add(AND);   
	operations.add(EQUALS_LOGICAL);   
        operations.add(EQUALS_NUMERIC);  
        operations.add(EQUALS_TEXTUAL);  
	operations.add(NOT_EQUALS_LOGICAL);   
        operations.add(NOT_EQUALS_NUMERIC);  
        operations.add(NOT_EQUALS_TEXTUAL);
	operations.add(LESS_EQUALS);  
	operations.add(LARGE_EQUALS);  
	operations.add(LESS);  
	operations.add(LARGE); 
	operations.add(PLUS);  
        operations.add(CONCAT);  
	operations.add(MINUS);   
        operations.add(MULTIPLY);
	operations.add(DIVIDE);
	operations.add(REMAINDER);  
	operations.add(NOT);  
	//operations.add(NEGATIVE);  
	operations.add(SCOPE);  
        
        operations.addAll(DefaultFunctions.getFunctions());
    }
    
    public static TreeSet<Operation> getOperations()
    {
	if(operations == null)
	{
	    init();
	}
	
	return operations;
    }
}
